Repository navigation
fix(metadata-protocol): a publish whose object the runtime registry refused names it under the advisory rule object-registry-refused - #33397
Conversation
…efused names it under the advisory rule object-registry-refused and logs it at error once The registry write-through hands the refusal back instead of keeping it to a warn line; runPublishSideEffects reports it once at error and returns the advisory, which publishPackageDrafts puts on the refused object's published[] element and publishMetaItem and a publish-mode saveMetaItem put on their own advisories. Outcome and success are unchanged. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cw787kMeaX7oyz7DDC
…spelling pin reads Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cw787kMeaX7oyz7DDC
…form at collection Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cw787kMeaX7oyz7DDC
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 672a0a4bb0cef66d6553304eb34b4941d7baa754 && git checkout 672a0a4bb0cef66d6553304eb34b4941d7baa754
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7e2eadd2902c3c934c7dcdfaf53206ad41db0b6e 2dfe890eee82a22d9850a1125e8c36db2a712b8b && git checkout -B drift-repro 7e2eadd2902c3c934c7dcdfaf53206ad41db0b6e && git merge --no-ff 2dfe890eee82a22d9850a1125e8c36db2a712b8b
node scripts/docs-audit/affected-docs.mjs --json 7e2eadd2902c3c934c7dcdfaf53206ad41db0b6e
|
|
domain:server#5 · os-elon-musk · session_016WV4cw787kMeaX7oyz7DDC Request: review · area A4 · head 2dfe890 Fixes #33005 (maintainer ruling D, 6879630490) · metadata-protocol, 6 files, +208/−11 · NOT governed. Declared Clause-② no; please judge whether the new advisory rule code makes it |
|
Contract-review-claim: session_016WV4cxDxAYxdtDhtR1kQQe · head 2dfe890 · 2026-11-14T18:52Z review#2 (server area pilot, A4). |
Review: PASSHead: Checked against the maintainer ruling (6879630490, option D):
Nits (optional, no card owed):
Generated by Claude Code |
|
domain:server#5 · os-elon-musk · session_016WV4cw787kMeaX7oyz7DDC ACCEPT · area A4 · head 2dfe890 · 2026-11-14T18:54Z
|
Requested by Elon · project thread
Fixes #33005
Clause-②: no
Metadata-protocol: none
维护者速读
publishPackageDrafts在该对象的published[]元素上带一条warningadvisory(稳定规则码object-registry-refused,where写明对象名),服务端打一次 error 日志(后果 + 修法);publishMetaItem与 publish 模式的saveMetaItem同样带这条 advisory。published/success: true,却没有任何地方点名这个对象,紧接着的种子加载就撞OBJECT_NOT_FOUND。按裁决选项 D:先告警,outcome 枚举与success不变(选项 A 另开卡)。packages/metadata-protocol/src/protocol-registry-hydration.ts(applyObjectRegistryMutation改为把拒收交回,新增reportObjectRegistryRefusal)·protocol-history-and-publish.ts(runPublishSideEffects与 Phase 2 的元素挂载)·protocol-publish-drafts-advisories.test.ts新增的 describe。pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 src/protocol-publish-drafts-advisories.test.ts,预期 10/10 通过;其中 Publish: a seed load right after publishPackageDrafts can hit OBJECT_NOT_FOUND for objects the data door serves a minute later — the registry write-through swallows a registerObject failure #33005 两条钉住规则码、对象名与 error 日志只出现一次。What changes
The registry write-through runs after the metadata commit (ADR-0067 D2). When
registerObjectthrew there,ProtocolRegistryHydrationFacet.applyObjectRegistryMutationcaught it and logged onlyconsole.warn, sopublishPackageDraftsansweredoutcome: 'published',success: true, with nothing naming the object. A seed load right after that publish then hitOBJECT_NOT_FOUND.This PR implements the maintainer's option D ruling (card comment 6879630490):
applyObjectRegistryMutationkeeps itswarnline and now also returns the refusal (ObjectRegistryRefusal, the thrown value) instead of discarding it.applyRegistryWriteThroughpasses it on from itsobjectbranch. Every other branch, and every other caller (rollback, revert, orphan adoption, remote mutation, batch registration), behaves as before and ignores the return value.reportObjectRegistryRefusal(name, refusal)and the stable rule idOBJECT_REGISTRY_REFUSED = 'object-registry-refused'. These follow the pattern ofobjectStorageSyncRefusedNotice(object-storage-sync-refused). The reporter logs oneconsole.errorline that states the consequence and the fix (AGENTS.md "Degradation log levels"), with the registry's reason as a separate argument. It returns onewarningin theRuntimeAuthoringIssueshape:where: object "NAME",path: ''. An undeclared failure's text stays out of the 2xx body (clientFacingFailureText).runPublishSideEffectsreports the refusal once per call and returns it asregistryRefused.publishPackageDraftsPhase 2 appends that advisory to the refused object'spublished[]element. It comes after the gate's findings and before any storage notice, and an element with no advisories still carries noadvisorieskey. Outcome andsuccessare unchanged.advisoriesfield:publishMetaItemand a publish-modesaveMetaItemadd the same advisory to their ownadvisories. That is about 8 extra lines.protocol.tsupdates the private method signatures of the three host stubs for the new return types. No spec orprotocol.zod.tschange; outcome enum untouched.Tests
New top-level describe in
packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts, using that file's shared stub harness:ticketanswerssuccess: true,outcome: 'published',publishedCount: 2.ticket's element carries exactly one advisory:rule: 'object-registry-refused',severity: 'warning',where: 'object "ticket"', the message names'ticket', the key set matches the other doors, and the undeclared reason is withheld. Exactly oneconsole.errorline namesticket, with the reason whole as its second argument. The siblinginvoiceelement has noadvisorieskey.publishMetaItemand a publish-modesaveMetaItemanswer the same rule with the rightwhere, and each logs once.The test file also gains a module-top
import '@objectstack/lint'(required bycheck:test-source-aliasfor an edited test file).Acceptance notes
origin/main4c9104e before any edit (scratch test, deleted):{"outcome":"published","success":true,"published":[{"type":"object","name":"ticket","version":"..."}]}, WARN[Protocol] registerObject failed for ticket: registry refused the object, ERROR[], oneregisterObjectcall.protocol-history-and-publish.ts. This diff's hunks there are one import line,runPublishSideEffects's write-through capture and return type, two lines inpublishMetaItem's advisory block, and one line in Phase 2's element loop. It uses the currentmainadvisory helpers and none of their hunks.protocol.ts(three stub return types) is outside the dispatch's file list, but inside the claim'spackages/metadata-protocol/src/**.object_extensionand overlay branches ofapplyRegistryWriteThrough, and the non-publish callers of the object write-through (rollback, revert, orphan adoption, remote mutation fan-out). They still log atwarnand answer nothing. The stricter reading ("neverpublishedwhile an object does not resolve") belongs to the follow-up option A card.SchemaRegistry,judgeBatchObjectRegistrationsregisters every promoted object insiderestoringOnRefusalin Phase 1, so a refusal that repeats every time already answersrefusedtruthfully. What reaches this advisory is a refusal that shows up only at Phase 2's real registration.Verification
Final head:
2dfe890ee(objectstack, branchclaude/area-A4-33005).All heavy runs went through
scripts/pm/os-verify-lock.sh(slota4-33005). Exit codes were captured before any pipe.Package suites
pnpm --filter @objectstack/metadata-protocol typecheck && pnpm --filter @objectstack/metadata-protocol testat2dfe890ee: typecheck clean,Test Files 339 passed | 9 skipped (348),Tests 22846 passed | 43 skipped (22889).tsc --listFilesOnlyincludes the edited test file.@objectstack/objectqlTest Files 595 passed (595),Tests 12946 passed;@objectstack/restTest Files 410 passed (410),Tests 8370 passed | 332 skipped. Both ran at474253a15; the only later commit adds one import line to a metadata-protocol test file. The other 80 downstream packages thatturbo ls --affectedlists are left to CI (Test Core). That set comes from the package dependency graph, so it cannot see tests that read another package's source from disk. No export of the package changed: the new symbols are not re-exported from its index.eslint --no-inline-configon the five touched source and test files: exit 0.Ablation (one-time proof, not kept as a test)
The fix was committed first.
node scripts/ablation-replace.mjsreplacedreturn { error: err };inapplyObjectRegistryMutationwithreturn undefined;; the anchor went 1 to 0 and the blob changed 7f147079c438 to afb04eb6c844. The pin file then failed,Tests 2 failed | 8 passed (10), both failures in the #33005 describe. The tool restored the file: blob back to 7f147079c438, equal to HEAD;git diff HEADempty;git status --porcelainempty. The test imports./protocol.jsfrom source, so no rebuild was needed.Gate families
The families
node scripts/pm/dispatch-gates.mjs --commandsderives (72) were run one by one. The run at474253a15turned up one real finding,check:test-source-alias: the edited test file needed a module-topimport '@objectstack/lint'. It was fixed in2dfe890eeand re-run green. At that pointcheck:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debthad not been measured (missing built output or a lock wait); all three were re-run at2dfe890eeand exited 0. The full union is being re-run at2dfe890ee; the result goes in the os-dev report comment on the card.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cw787kMeaX7oyz7DDC